package fms.controller;

import fms.dao.CptInfoService;
import fms.dao.PlanCptWdService;
import fms.model.CptInfo;
import fms.model.PlanCptWd;
import fms.model.PlanFlightInfoBuff;
import fms.util.*;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * <P>FileName: CQFlyPlanParseController.java
 *
 * @param
 *         <P>Description: 解析飞行计划服务
 *         <P>Version: v1.0
 *         <P>History:
 */
public class CQFlyPlanParseController implements IFlyPlanParseController {
    private Logger logger = Logger.getLogger(CQFlyPlanParseController.class);

    // 存入CPT_INFO\PLAN_CPT_WD表的记录列表数据
    private List cptInfoList = new ArrayList();
    private List planCptWdList = new ArrayList();

    private JetplanParseRuleVO paramVO = new JetplanParseRuleVO();
    private Map<String, String> mapAirlinesCodes = new HashMap<String, String>();

    // 提供数据库交互服务的对象
    private CptInfoService cptInfoService = null;
    private PlanCptWdService planCptWdService = null;

    // 航班号、起飞机场、降落机场
    private final String regexFlightnumDepArr = "FLGT NO: (.+?)\\s+?ROUTE: (.+?)-(.+?)\\s+?[\\s\\S]*";
    // 记录正则regexFlightnumDepArr是否匹配成功
    private boolean bFlightnumDepArr = false;

    // 机尾号
    private final String regexAircraftid = "AC REG.: (.+?)\\s+?.+?";
    // 记录正则regexAircraftid是否匹配成功
    private boolean bAircraftid = false;

    // 航路点高度层
    private final String regexCptAlt = "[\\s\\S]*FL(\\w+)\\s+?FL(\\w+)\\s+?FL(\\w+)\\s+?FL(\\w+)\\s+?FL(\\w+)\\s+?FL(\\w+)";
    // 记录正则regexCptAlt是否匹配成功
    private boolean bCptAlt = false;

    // 航路点位置及个高度层风速风向
    private final String regexCptPosWindFuzzy = "\\d{2}\\s+?(\\w+?)\\s+?([NS]\\w+)\\s+?([EW]\\w+)\\s+?(\\w+/\\w+)\\s+?" +
            "(\\w+/\\w+)\\s+?(\\w+/\\w+)\\s+?(\\w+/\\w+)\\s+?(\\w+/\\w+)\\s+?(\\w+/\\w+)";

    // 为了过滤出“经度、纬度、风速、风向”位数过多过少不合法情况，进行两次匹配规则
    // 航路点位置及个高度层风速风向（二轮：精确匹配）
//    private final String regexCptPosWindExact = "\\d{2}\\s+?(\\w+?)\\s+?([NS]\\d{5})\\s+?([EW]\\d}{6})\\s+?(\\w{3}/\\w{3})\\s+?" +
//            "(\\w{3}/\\w{3})\\s+?(\\w{3}/\\w{3})\\s+?(\\w{3}/\\w{3})\\s+?(\\w{3}/\\w{3})\\s+?(\\w{3}/\\w{3})";

    // 记录正则regexCptPosWind是否匹配成功
    private boolean bCptPosWind = false;

    // 航班计划时间
    private final String regexPlanTime = "[\\s\\S]*(\\d{4}/\\d{1,2}/\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2})";
    // 记录正则regexPlanTime是否匹配成功
    private boolean bPlanTime = false;

    // 飞行计划中的高度层数
    public static final int NumAlt = 6;

    // 飞行计划字符串编码方式
    private final String StrEncoding =  "UTF-8";


    // 读取配置文件，加载解析TXT型飞行计划所需的参数信息
    public CQFlyPlanParseController() {
        cptInfoService = ConfUtil.getSingletonApplicationContextDao().getBean("cptInfoService", CptInfoService.class);
        planCptWdService = ConfUtil.getSingletonApplicationContextDao().getBean("planCptWdService", PlanCptWdService.class);

        File fParseRule = null;
        fParseRule = new java.io.File(ConfUtil.props.getProperty("parseRuleFilePath"));
        final SAXReader reader = new SAXReader();
        try {
            logger.info("（TXT飞行计划解析）航空公司三字码和二字码对照表加载中！");
            final Document paraseRulenDoc = reader.read(fParseRule);
            List airlineCodeList = paraseRulenDoc.selectNodes("/FPLParseRules/TxtplanParseRule/AirlinesFlags/AirlineCode");
            Iterator it = airlineCodeList.iterator();
            while (it.hasNext()) {
                Element airlinesCode = (Element) it.next();
                mapAirlinesCodes.put(airlinesCode.attributeValue("id"), airlinesCode.getTextTrim());
            }
            logger.info("（TXT飞行计划解析）航空公司三字码和二字码对照表加载结束！");
        } catch (final Exception e) {
            logger.error(e.getMessage());
        }
    }

    @Override
    public boolean parseDataSource(File fDataSource) {
        return false;
    }

    /**
     * 算法细节描述：
     * 单个航路点的某一个高度层的风速、风向不和法（位数、内容），则丢弃该高度层的风速、风向信息；
     * 航路点经度纬度不合法（位数、内容），则丢弃整个航路点信息；
     *
     * 接口变更说明：
     *    由于最初开发时，使用FILE接口，反复三次打开文件，以便正则匹配出不同的数据；
     *    现在，更改成strDataSource方式后，反复三次将字符串转换为输入流
     *
     * @param strDataSource : 用于存储飞行计划全文
     */
    @Override
    public boolean parseDataSource(String strDataSource) {
        BufferedReader bufReader = null;
        String strLine = null;
        ByteArrayInputStream stringInputStream = null;

        try {
//            if (!fDataSource.exists() || fDataSource.isDirectory())
//                throw new FileNotFoundException();

            // 用于缓存反复在记录中出现的字段数据
            PlanFlightInfoBuff planFlightInfoBuff = new PlanFlightInfoBuff();
            Matcher matcher = null;
            Pattern pattern = null;

            // 第一轮分析：从map中获取配置的代表航空公司的字符串，匹配出该飞行计划所属公司的二字码
//            bufReader = new BufferedReader(new FileReader(fDataSource));
//            bufReader = new BufferedReader(isrDataSource);
            stringInputStream = new ByteArrayInputStream(strDataSource.getBytes());
            bufReader = new BufferedReader(new InputStreamReader(stringInputStream, StrEncoding));
            strLine = bufReader.readLine();
            boolean bFind = false;
            for (String key : mapAirlinesCodes.keySet()) {
                String regexAirline = "[\\s\\S]*" + key + "[\\s\\S]*";
                // 按行匹配航空公司标识串以确认二字码
                while (null != strLine) {
                    pattern = Pattern.compile(regexAirline);
                    matcher = pattern.matcher(strLine);
                    if (matcher.matches()) {
                        planFlightInfoBuff.setAirlines(mapAirlinesCodes.get(key));
                        bFind = true;
                        break;
                    }
                    strLine = bufReader.readLine();
                }
                if (bFind) {
                    break;
                }
            }
            if (!bFind) {
                logger.error("无法确认该飞行计划所属航空公司二字码！");
                return false;
            }

            if (bufReader != null)
                bufReader.close();
            if (stringInputStream != null)
                stringInputStream.close();

            // 第二轮分析：获取飞行计划中CPT_INFO\PLAN_CPT_WD量表中每条记录对应的恒定信息（除航空公司二字码）
//            bufReader = new BufferedReader(new FileReader(fDataSource));
//            bufReader = new BufferedReader(fDataSource);
//System.out.println(strBuilderDataSource.toString());
            stringInputStream = new ByteArrayInputStream(strDataSource.getBytes());
            bufReader = new BufferedReader(new InputStreamReader(stringInputStream, StrEncoding));
            strLine = bufReader.readLine();
            while (null != strLine) {
                // 匹配出航班号、起飞机场、降落机场
                if (ObjectUtil.isBlank(planFlightInfoBuff.getFlightId())) {
                    pattern = Pattern.compile(regexFlightnumDepArr);
                    matcher = pattern.matcher(strLine);
                    if (matcher.matches()) {
                        if (matcher.group(1).trim().equals("")
                                || matcher.group(2).trim().equals("")
                                || matcher.group(3).trim().equals("")) {
                            logger.error("航班航、起飞机场、落地机场存在空字符串！");
                            return false;
                        } else {
                            planFlightInfoBuff.setFlightId(matcher.group(1));
                            // System.out.println( "-" + planFlightInfoBuff.getFlightId() + "-" );
                            planFlightInfoBuff.setDep(matcher.group(2));
                            planFlightInfoBuff.setArr(matcher.group(3));
                            bFlightnumDepArr = true;
                        }
                    }
                }
                // 机尾号
                if (ObjectUtil.isBlank(planFlightInfoBuff.getAircraftId())) {
                    pattern = Pattern.compile(regexAircraftid);
                    matcher = pattern.matcher(strLine);
                    if (matcher.matches()) {
                        if (matcher.group(1).trim().equals("")) {
                            logger.error("机尾号为空字符串！");
                            return false;
                        } else {
                            planFlightInfoBuff.setAircraftId(matcher.group(1));
                            bAircraftid = true;
                        }
                    }
                }
                // 飞行计划时间
                if (null == planFlightInfoBuff.getPlanDate()) {
                    pattern = Pattern.compile(regexPlanTime);
                    matcher = pattern.matcher(strLine);
                    if (matcher.matches()) {
                        if (matcher.group(1).trim().equals("")) {
                            logger.error("飞行计划时间为空字符串！");
                            return false;
                        } else {
                            planFlightInfoBuff.setPlanDate(DateUtil.StrToDate(matcher.group(1), SysConstants.FORMAT_DATETIME_FULL_));
                            bPlanTime = true;
                        }
                    }
                }
                // 飞行计划中涉及的高度层
                pattern = Pattern.compile(regexCptAlt);
                matcher = pattern.matcher(strLine);
                if (matcher.matches()) {
                    String[] arrAlt = new String[NumAlt];
                    for (int i = 1; i < NumAlt + 1; i++) {
                        if (matcher.group(i).trim().equals("")) {
                            logger.error("部分高度层为空字符串！");
                            return false;
                        } else {
                            arrAlt[i - 1] = matcher.group(i);
                            bCptAlt = true;
                        }
                    }
                    planFlightInfoBuff.setAltArray(arrAlt);
                }
                strLine = bufReader.readLine();
            }

            if (bufReader != null)
                bufReader.close();
            if (stringInputStream != null)
                stringInputStream.close();

            // 整个飞行计划遍历一遍，没有匹配到某项非空信息后，飞行计划解析失败
            if (!bFlightnumDepArr) {
                logger.error("航班航、起飞机场、落地机场匹配失败！");
                return false;
            } else if (!bAircraftid) {
                logger.error("机尾号匹配失败！");
                return false;
            } else if (!bPlanTime) {
                logger.error("飞行计划时间匹配失败！");
                return false;
            } else if (!bCptAlt) {
                logger.error("高度层匹配失败！");
                return false;
            }

            // 第三轮分析：利用上一轮恒定信息和新的匹配到的航路点位置、风向风速信息组成两表记录List
//            bufReader = new BufferedReader(new FileReader(fDataSource));
//            bufReader = new BufferedReader(fDataSource);
            stringInputStream = new ByteArrayInputStream(strDataSource.getBytes());
            bufReader = new BufferedReader(new InputStreamReader(stringInputStream, StrEncoding));
            strLine = bufReader.readLine();
            while (null != strLine) {
                pattern = Pattern.compile(regexCptPosWindFuzzy);
                matcher = pattern.matcher(strLine);
                if (matcher.matches()) {
                    CptInfo cptInfo = new CptInfo();
                    if (matcher.group(1).trim().equals("")
                            || !ValidCheckAviation.RegexCheck(matcher.group(2).trim(), ValidCheckAviation.CheckType.WindLat.ordinal())
                            || !ValidCheckAviation.RegexCheck(matcher.group(3).trim(), ValidCheckAviation.CheckType.WindLon.ordinal())
                            ) {
                        logger.error("航路点、经度、纬度不合法，本航路点信息已丢弃：" + strLine);
                        strLine = bufReader.readLine();
                        continue;
                    }

                    cptInfo.setCptName(matcher.group(1));
                    cptInfo.setLat(matcher.group(2));
                    cptInfo.setLon(matcher.group(3));
                    cptInfo.setFlightId(planFlightInfoBuff.getFlightId());
                    cptInfo.setAircraftId(planFlightInfoBuff.getAircraftId());
                    cptInfo.setAirlines(planFlightInfoBuff.getAirlines());
                    cptInfo.setArr(planFlightInfoBuff.getArr());
                    cptInfo.setDep(planFlightInfoBuff.getDep());
                    cptInfo.setPlanDate(planFlightInfoBuff.getPlanDate());

                    cptInfoList.add(cptInfo);

                    for (int i = 1; i < NumAlt + 1; i++) {
                        PlanCptWd planCptWd = new PlanCptWd();
                        planCptWd.setAlt(Float.valueOf(planFlightInfoBuff.getAltArray()[i - 1]));
                        // 前三个分组的数据被上面的if语句中使用，之后的数据才是不同高度层的风向风速信息
                        String strGroup = matcher.group(3 + i);

                        if (!ValidCheckAviation.RegexCheck(strGroup.split("/")[0].trim(), ValidCheckAviation.CheckType.WindDir.ordinal(), "\\d{3}")
                                || !ValidCheckAviation.RegexCheck(strGroup.split("/")[1].trim(), ValidCheckAviation.CheckType.WindVel.ordinal())
                                ) {
                            logger.error("本行航路点风速、风向不合法：" + strLine);
                            continue;
                        }
                        planCptWd.setWindDir(Integer.valueOf(strGroup.split("/")[0]));
                        planCptWd.setWindVel(Integer.valueOf(strGroup.split("/")[1]));
                        planCptWd.setCptName(matcher.group(1));
                        planCptWd.setFlightId(planFlightInfoBuff.getFlightId());
                        planCptWd.setPlanDate(planFlightInfoBuff.getPlanDate());
                        planCptWd.setAircraftId(planFlightInfoBuff.getAircraftId());
                        planCptWd.setAirlines(planFlightInfoBuff.getAirlines());

                        planCptWdList.add(planCptWd);
                    }
                }// 第一轮：模糊匹配  if (matcher.matches())
                strLine = bufReader.readLine();
            }
        } catch (Exception e) {
            logger.error("春秋TXT格式飞行计划解析异常:" + e.toString());
            return false;
        } finally {
            try {
                if (bufReader != null)
                    bufReader.close();
                if (stringInputStream != null)
                    stringInputStream.close();
            } catch (IOException ex) {
                logger.error("流关闭失败");
                return false;
            }
        }
        return true;
    }

    @Override
    public void saveToDB() {
         try {
            // CPT_INFO 数据入库
            cptInfoService.save(cptInfoList);
            // PLAN_CPT_WD 数据入库
            planCptWdService.save(planCptWdList);
        } catch (Exception e) {
            logger.error("数据库访问异常：" + e.getMessage());
        }
    }

}

/*
留着下面代码，调试时使用；created by GuoXY when 2014/7/2
if (matcher.matches()) {
        // regexFlightnumDepArr测试
        System.out.println("=====>" + matcher.group(1) + ":" + matcher.group(2) + ":" + matcher.group(3)
        + ":" + matcher.group(4));
        break;
        // regexAircraftid、regexPlanTime测试
        System.out.println("=====>" + matcher.group(1));
        break;
        // regexCptAlt测试
        System.out.println("=====>" + matcher.group(1) + ":" + matcher.group(2) + ":" + matcher.group(3)
        + ":" + matcher.group(4) + ":" + matcher.group(5));
        break;
        // regexCptPosWind测试
        System.out.println("=====>" + matcher.group(1) + ":" + matcher.group(2) + ":" + matcher.group(3)
        + ":" + matcher.group(4) + ":" + matcher.group(5) + ":" + matcher.group(6) + ":"
        + matcher.group(7) + ":" + matcher.group(8));
        break;
}
*/